﻿using Clancy.Interfaces;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Threading.Tasks;

namespace Clancy.Bl
{
    internal class RecordingManager : IRecordingManager, IDisposable
    {
        private readonly List<IDisposable> m_Disposables = new List<IDisposable>();
        private readonly Dictionary<Guid, FileSystemWatcher> m_FileSystemWatchers = new Dictionary<Guid, FileSystemWatcher>();
        private readonly ObservableCollection<IRecording> m_RecordingCollection = new ObservableCollection<IRecording>();
        private readonly IRecordingItemFactory m_RecordingItemFactory;
        private readonly ISurveilanceStorage m_SurveilanceStorage;

        public RecordingManager(ISurveilanceStorage surveilanceStorage, IRecordingItemFactory recordingItemFactory)
        {
            m_SurveilanceStorage = surveilanceStorage;
            m_RecordingItemFactory = recordingItemFactory;

            AttachToSurveilanceLogic();

            InitializeRecordingArchive();
        }

        public ObservableCollection<IRecording> RecordingCollection
        {
            get { return m_RecordingCollection; }
        }

        public Task<bool> DeleteRecording(Guid selectedRecordingId)
        {
            IRecording recording = m_RecordingCollection.FirstOrDefault(rec => rec.Id == selectedRecordingId);
            if (recording == null)
                return Task.FromResult(false);

            if (File.Exists(recording.FilePath))
            {
                try
                {
                    File.Delete(recording.FilePath);
                }
                catch (Exception)
                {
                    Task.FromResult(false);
                }
            }

            m_RecordingCollection.Remove(recording);
            return Task.FromResult(true);
        }

        public void Dispose()
        {
            foreach (IDisposable dispo in m_Disposables)
            {
                dispo.Dispose();
            }
            m_Disposables.Clear();
        }

        public async void InsertNewRecord(ISurveilanceObject survObj, string recordFile)
        {
            await InsertNewFile(survObj, recordFile);
        }

        private void AttachToSurveilanceLogic()
        {
            IObservable<EventPattern<NotifyCollectionChangedEventArgs>> collectionChangedObservable =
                Observable.FromEventPattern<NotifyCollectionChangedEventArgs>(m_SurveilanceStorage.SurveilanceObjects, "CollectionChanged");

            m_Disposables.Add(collectionChangedObservable
                .Where(change => change.EventArgs.Action == NotifyCollectionChangedAction.Add)
                .Select(change => change.EventArgs.NewItems)
                .Subscribe(async items =>
                {
                    foreach (ISurveilanceObject survObj in items)
                    {
                        await ScanRecordingArchive(survObj);
                    }
                }));

            m_Disposables.Add(collectionChangedObservable
                .Where(change => change.EventArgs.Action == NotifyCollectionChangedAction.Remove)
                .Select(change => change.EventArgs.OldItems)
                .Subscribe(items =>
                {
                    foreach (ISurveilanceObject survObj in items)
                    {
                        DetachToSurveilanceResults(survObj);
                    }
                }));
        }

        private void DetachToSurveilanceResults(ISurveilanceObject survObj)
        {
            if (!m_FileSystemWatchers.ContainsKey(survObj.Id))
                return;

            m_FileSystemWatchers[survObj.Id].Dispose();
        }

        private async void InitializeRecordingArchive()
        {
            foreach (ISurveilanceObject survObj in m_SurveilanceStorage.SurveilanceObjects)
            {
                await ScanRecordingArchive(survObj);
            }
        }

        private async Task InsertNewFile(ISurveilanceObject survObj, string fullPath)
        {
            m_RecordingCollection.Add(await m_RecordingItemFactory.CreateRecordingItem(survObj, fullPath));
        }

        private async Task ScanRecordingArchive(ISurveilanceObject survObj)
        {
            if (!Directory.Exists(survObj.RecordBaseDirectory))
                return;

            foreach (string filepath in Directory.GetFiles(survObj.RecordBaseDirectory, "*.avi"))
            {
                await InsertNewFile(survObj, filepath);
            }
        }
    }
}